﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Activity.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Describes and manipulates the activity.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System.Collections.Generic;
using Diagnostics.Annotations;

namespace MobilePal.State
{
  #region Imported Namespaces


  using System;
  using System.Diagnostics;
  using System.Text;
  using System.Xml.Serialization;
  using Collections;

  #endregion


  /// <summary>
  /// Describes and manipulates the activity.
  /// </summary>
  [UsedImplicitly(ImplicitUseFlags.IncludeMembers)]
  public class Activity
  {
    #region Constants and Fields


    [NotNull]
    private readonly ActivityCategoryCollection _categories = new ActivityCategoryCollection();

    private bool _isInProgress;

    [NotNull]
    private string _name;


    #endregion


    #region Constructors and Destructors


    /// <summary>
    /// Initializes a new instance of the <see cref="Activity"/> class.
    /// </summary>
    public Activity()
    {
      _categories.CollectionChanged += CategoriesCollectionChanged;
      LastToggleTime = DateTime.MinValue;
    }


    /// <summary>
    /// Initializes a new instance of the <see cref="Activity"/> class.
    /// </summary>
    /// <param name="name">The activity name.</param>
    /// <exception cref="ArgumentNullException"><c>name</c> is <c>null</c>.</exception>
    public Activity([NotNull] string name)
      : this()
    {
      if (name == null)
      {
        throw new ArgumentNullException("name");
      }

      _name = name;
    }

      /// <summary>
      /// Initializes a new instance of the <see cref="Activity"/> class.
      /// </summary>
      /// <param name="name">The activity name.</param>
      /// <param name="categories">Add categories to the activity</param>
      /// <exception cref="ArgumentNullException"><c>name</c> is <c>null</c>.</exception>
      public Activity([NotNull] string name, params ActivityCategory[] categories)
        : this(name, (IEnumerable<ActivityCategory>)categories)
        {
        }

      /// <summary>
      /// Initializes a new instance of the <see cref="Activity"/> class.
      /// </summary>
      /// <param name="name">The activity name.</param>
      /// <param name="categories">Add categories to the activity</param>
      /// <exception cref="ArgumentNullException"><c>name</c> is <c>null</c>.</exception>
      public Activity([NotNull] string name, IEnumerable<ActivityCategory> categories)
          : this(name)
      {
          if (categories != null)
          {
              Categories.AddRange(categories);
          }
      }


    #endregion


    #region Events


    /// <summary>
    /// Occurs when category collection has changed.
    /// </summary>
    public event EventHandler<NotifyCollectionChangedEventArgs<ActivityCategory>> CategoryCollectionChanged;

    /// <summary>
    /// Occurs when is in progress flag changed.
    /// </summary>
    public event EventHandler<ActivityStateEventArgs> IsInProgressChanged;

    /// <summary>
    /// Occurs when is in progress flag changing.
    /// </summary>
    public event EventHandler<ActivityStateEventArgs> IsInProgressChanging;

    /// <summary>
    /// Occurs before is in progress flag starts changing.
    /// </summary>
    public event EventHandler<CancelActivityStateEventArgs> PreviewIsInProgressChanging;


    #endregion


    #region Properties


    /// <summary>
    /// Gets the list of activity categories.
    /// </summary>
    /// <value>
    /// The list of activity categories.
    /// </value>
    [NotNull]
    public ActivityCategoryCollection Categories
    {
      get { return _categories; }
    }


    /// <summary>
    /// Gets or sets a value indicating whether the <see cref="Activity"/> is in progress.
    /// </summary>
    /// <value>
    /// Returns <c>true</c> if <see cref="Activity"/> is in progress; otherwise, <c>false</c>.
    /// </value>
   
    public bool IsInProgress
    {
      get
      {
        return _isInProgress;
      }

      set
      {
        SetIsInProgress(value, DateTime.Now);
      }
    }

    /// <summary>
    /// Gets or sets the last toggle time.
    /// </summary>
    /// <value>
    /// The last toggle time.
    /// </value>
   
    public DateTime LastToggleTime
    {
      get;
      set;
    }

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>
    /// The name of the activity.
    /// </value>
   
    [NotNull]
    public string Name
    {
      get
      {
        return _name;
      }

      set
      {
        Assert.ArgumentNotNull(value, "value");

        _name = value;
      }
    }


    #endregion


    #region Public Methods


    /// <summary>
    /// Dumps the activity.
    /// </summary>
    /// <param name="separator">
    /// The separator.
    /// </param>
    /// <returns>
    /// An activity dump string.
    /// </returns>
    [NotNull]
    public string Dump([NotNull] string separator)
    {
      Assert.ArgumentNotNull(separator, "separator");

      var stringBuilder = new StringBuilder();
      stringBuilder.Append(Name);
      stringBuilder.Append(separator);
      stringBuilder.Append(IsInProgress);
      stringBuilder.Append(separator);
      stringBuilder.Append(LastToggleTime);

      return stringBuilder.ToString();
    }


    /// <summary>
    /// Toggles the is in progress flag.
    /// </summary>
    public void ToggleIsInProgress()
    {
      SetIsInProgress(!IsInProgress, DateTime.Now);
    }


    /// <summary>
    /// Toggles the is in progress flag.
    /// </summary>
    /// <param name="toggleTime">
    /// The toggle time.
    /// </param>
    public void ToggleIsInProgress(DateTime toggleTime)
    {
      SetIsInProgress(!IsInProgress, toggleTime);
    }


    /// <summary>
    /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
    /// </returns>
    [NotNull]
    public override string ToString()
    {
      return string.Format("[Activity: {0}]", Name);
    }


    #endregion


    #region Methods


    private void CategoriesCollectionChanged(object sender, NotifyCollectionChangedEventArgs<ActivityCategory> e)
    {
      if (CategoryCollectionChanged != null)
      {
        CategoryCollectionChanged(this, e);
      }
    }


    private void SetIsInProgress(bool value, DateTime toggleTime)
    {
      if (value == _isInProgress)
      {
        return;
      }

      if (PreviewIsInProgressChanging != null)
      {
        var eventArgs = new CancelActivityStateEventArgs(this, toggleTime);

        PreviewIsInProgressChanging(this, eventArgs);

        if (eventArgs.Cancel)
        {
          return;
        }
      }

      if (IsInProgressChanging != null)
      {
        IsInProgressChanging(this, new ActivityStateEventArgs(this, toggleTime));
      }

      _isInProgress = value;
      LastToggleTime = toggleTime;

      if (IsInProgressChanged != null)
      {
        IsInProgressChanged(this, new ActivityStateEventArgs(this, toggleTime));
      }
    }


    #endregion
  }
}